home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Temp / MiscSerialPort2.m < prev    next >
Text File  |  1995-07-25  |  13KB  |  517 lines

  1. /*
  2. MiscSerialPort.m -- an OO wrapper around the serial ports
  3. Written by Matt Brandt Copyright ( c ) 1994 by Matt Brandt.
  4. Modified by Arnaud Meuret
  5. Version 2.0.  All rights reserved.
  6. This notice may not be removed from this source code.
  7.  
  8.     This object is included in the MiscKit by permission from the author
  9.     and its use is governed by the MiscKit license, found in the file
  10.     "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  11.     for a list of all applicable permissions and restrictions.
  12. ______________________________________________________________________________*/
  13.  
  14. #import "MiscSerialPort.h"
  15.  
  16. struct
  17. {
  18.     int        intValue;
  19.     int        name;
  20. } baudRateTable[] = {
  21.     {110,B110},
  22.     {300,B300},
  23.     {600,B600},
  24.     {1200,B1200},
  25.     {2400,B2400},
  26.     {4800,B4800},
  27.     {9600,B9600},
  28.     {14400,B14400},
  29.     {19200,B19200},
  30.     {28800,B28800},
  31.     {38400,B38400},
  32.     {43200,B43200},
  33.     {57600,B57600},
  34.     {"",0}};
  35.  
  36. int                    origLineDisc;
  37. struct sgttyb        origTtyParms;
  38. struct tchars        origSpecChars;
  39. struct ltchars        origLocSpecChars;
  40. int                    origLocMode;
  41.  
  42. @implementation MiscSerialPort
  43.  
  44. //========================================================================
  45. - init
  46. {
  47.     ttyParms.sg_flags = 0;                    // clear all delay selection
  48.     ioctl( portFD,TIOCEXCL,&ttyParms );        // exclusive access
  49.     ioctl( portFD,TIOCHPCL,&ttyParms );        // hang up on close
  50.     
  51.     // These set the default behaviour of the instances
  52.     [self setDeviceName:"/dev/cufa"];        // port A (??name?? on white) with hard flow control
  53.     flags.connected = NO;                    // not connected
  54.     flags.suspended = NO;                    // not suspended
  55.     delegate = nil;                            // no default delegate
  56.     [self setBaud:B9600];                    // 9600 bauds
  57.     [self setParity:MISC_SP_NONE];            // no parity
  58.     [self setMode:MISC_SP_COOKED];            // full I/O processing
  59.     [self translateNL:YES];                    // translate NewLines
  60.     [self setEchoing:NO];                    // do not echo received chars
  61.     [self setFlowControl:MISC_SP_HARDFC];    // assume hardware flow control
  62.     return self;
  63. }
  64.  
  65. //========================================================================
  66. - free
  67. {
  68.     [self disconnect];
  69.     return [super free];        // so long and thanks for all the fish.
  70. }
  71.  
  72. //========================================================================
  73. - ( BOOL )connect
  74. {
  75.     int ldisc = NTTYDISC;
  76.  
  77.     if( flags.connected )            // do nothing if already connected
  78.         return YES;
  79.  
  80.     if( ![self deviceIsAvailable:device])
  81.     {
  82.         status = MISC_SP_CANT_LOCK;
  83.         return NO;
  84.     }
  85.         
  86.     portFD = open( device,O_RDWR );
  87.     if( portFD < 0 )
  88.         return NO;
  89.  
  90.     // Save current parameters
  91.     ioctl( portFD, TIOCGETD, &origLineDisc );
  92.     ioctl( portFD, TIOCGETP, &origTtyParms );
  93.     ioctl( portFD, TIOCGETC, &origSpecChars );
  94.     ioctl( portFD, TIOCLGET, &origLocMode );
  95.     ioctl( portFD, TIOCGLTC, &origLocSpecChars );
  96.  
  97.     ttyParms = origTtyParms;
  98.                     
  99.     // set new discipline
  100.     ioctl( portFD, TIOCSETD, &ldisc );
  101.     // pass our parameters structure to the driver ( I/O buffers are flushed )
  102.     ioctl( portFD, TIOCSETD, &ttyParms );
  103.     
  104.     flags.connected = YES;
  105.         
  106.     // setup file descriptor monitor, the function we register willl be called when something
  107.     // pops up on the port.
  108.     DPSAddFD( portFD, ( DPSFDProc )portEventHandler, self, NX_MODALRESPTHRESHOLD );
  109.     flags.suspended = NO;
  110.     
  111.     status = MISC_SP_OK;
  112.     return YES;
  113. }
  114.  
  115. //========================================================================
  116. - disconnect
  117. {
  118.     if( !flags.connected )
  119.         return self;
  120.  
  121.     if( !flags.suspended )
  122.         DPSRemoveFD( portFD );
  123.  
  124.     // Be a good object and put everything back as it was when we took it!
  125.     ioctl( portFD, TIOCSETD, &origLineDisc );
  126.     ioctl( portFD, TIOCSETP, &origTtyParms );
  127.     ioctl( portFD, TIOCSETC, &origSpecChars );
  128.     ioctl( portFD, TIOCLSET, &origLocMode );
  129.     ioctl( portFD, TIOCSLTC, &origLocSpecChars );
  130.  
  131.     // O.K. I dont bother setting back the "exclusive-use" bit if it has
  132.     // changed, but does it really matter? Anyway, the "hang-up-on-close" bit
  133.     // does not seem to be reset-able !
  134.  
  135.     if(close( portFD )<0)
  136.     {
  137.         status = MISC_SP_SEE_ERRNO;
  138.         return self;
  139.     }
  140.     flags.connected = NO;
  141.     status = MISC_SP_OK;
  142.     return self;
  143. }
  144.  
  145. //========================================================================
  146. // The convention is to have the accessor and modifier methods use
  147. // the ivar name, however setDevice is a method declared in NXSoundStream!
  148. - setDeviceName: ( const char * )name
  149. {
  150.     if( strcmp( name,device ) == 0 )
  151.         return self;
  152.         
  153.     strncpy( device,name,DEVICE_PATH_LEN );
  154.     device[DEVICE_PATH_LEN+1] = '\0';
  155.  
  156.     if( ![self deviceIsAvailable:name])
  157.     {
  158.         status = (MISC_SP_CANT_LOCK & MISC_SP_NO_DEVICE & MISC_SP_NOT_OPEN);
  159.         return NO;
  160.     }
  161.  
  162.     if( flags.connected  )
  163.     {
  164.         [self disconnect];
  165.         [self connect];
  166.     }
  167.     return self;
  168. }
  169.  
  170. //========================================================================
  171. - ( BOOL ) deviceIsAvailable: ( const char * )name
  172. {
  173.     id        lockFilename;
  174.     
  175.     lockFilename = [[MiscString alloc] initString:"/usr/spool/uucp/LCK/LCK.."];
  176.     
  177.     // concat the name of the device to the lockfile name
  178.     [lockFilename concatenate:[[lockFilename filename] stringValueAndFree]];
  179.  
  180.     miscLock = [[MiscLockFile alloc] init];
  181.     [miscLock setFileName:lockFilename];
  182.     
  183.     if ([miscLock lock] )
  184.     {
  185.         [miscLock unlock];
  186.         return YES;
  187.     }
  188.     else
  189.         return NO;
  190. }
  191.  
  192. //========================================================================
  193. - setFlowControl:( int )method;
  194. {
  195.     switch( method )
  196.     {
  197.     case MISC_SP_NOFC:
  198.         ttyParms.sg_flags &= ~TANDEM;        // clear XON/XOFF Flow control bit
  199.         flags.hardwareFC = NO;
  200.         break;
  201.  
  202.     case MISC_SP_HARDFC:
  203.         ttyParms.sg_flags &= ~TANDEM;        // clear XON/XOFF Flow control bit
  204.         flags.hardwareFC = YES;
  205.         break;
  206.  
  207.     case MISC_SP_SOFTFC:
  208.         ttyParms.sg_flags |= TANDEM;        // set XON/XOFF Flow control bit
  209.         flags.hardwareFC = NO;
  210.         break;
  211.         
  212.     default:
  213.         ttyParms.sg_flags &= ~TANDEM;        // clear XON/XOFF Flow control bit
  214.         flags.hardwareFC = YES;
  215.         break;
  216.     }
  217.         
  218.     if( flags.connected  )
  219.         ioctl( portFD,TIOCSETP,&ttyParms );
  220.  
  221.     status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
  222.     return self;
  223. }
  224.  
  225. //========================================================================
  226. - (int) flowControl;
  227. {
  228.     if( flags.hardwareFC )
  229.         return MISC_SP_HARDFC;
  230.         
  231.     if( ttyParms.sg_flags & TANDEM )
  232.         return MISC_SP_SOFTFC;
  233.     else
  234.         return MISC_SP_NOFC;
  235. }
  236.  
  237. //========================================================================
  238. - dropDTR
  239. {
  240.     if( flags.connected )
  241.         ioctl( portFD, TIOCCDTR,0 );
  242.     status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
  243.     return self;
  244. }
  245.  
  246. //========================================================================
  247. - raiseDTR
  248. {
  249.     if( flags.connected )
  250.         ioctl( portFD, TIOCSDTR,0 );
  251.     status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
  252.     return self;
  253. }
  254.  
  255. //========================================================================
  256. - setBaudRateByName: ( int )name
  257. {        
  258.     ttyParms.sg_ispeed = name;
  259.     ttyParms.sg_ospeed = name;
  260.     if( flags.connected )
  261.         ioctl( portFD,TIOCSETP,&ttyParms );
  262.     status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
  263.     return self;
  264. }
  265.  
  266. //========================================================================
  267. - setBaudRate: ( int )speed
  268. {
  269. UBYTE        i=0;
  270.  
  271.     // we try to find the first standard speed at least equal to what is requested
  272.     speed = ( int ) ceil( speed );
  273.     
  274.     while( baudRateTable[i].name )
  275.     {
  276.         if( baudRateTable[i].intValue >=speed )
  277.             return [self setBaud: baudRateTable[i].name];
  278.         i++;
  279.     }
  280.     [self setBaud: B19200];
  281.     return self;
  282. }
  283.  
  284. //========================================================================
  285. - setBaudRateFromString: ( const char* )speed
  286. {
  287.     // I thought of doing an sprintf on speed to check for a numerical value but it would probably
  288.     // be useful in 1 case over 42:-).
  289.     return [self setBaudRate:atoi( speed )];
  290. }
  291.  
  292. //========================================================================
  293. - setParity: ( int )parity
  294. {
  295.     switch( parity )
  296.     {
  297.     case MISC_SP_NONE:
  298.         ttyParms.sg_flags &= ( ~EVENP | ~ODDP );
  299.         break;
  300.  
  301.     case MISC_SP_ODD:
  302.         ttyParms.sg_flags |= ODDP;
  303.         ttyParms.sg_flags &= ~EVENP;
  304.         break;
  305.  
  306.     case MISC_SP_EVEN:
  307.         ttyParms.sg_flags |= EVENP;
  308.         ttyParms.sg_flags &= ~ODDP;
  309.         break;
  310.         
  311.     default:
  312.         ttyParms.sg_flags &= ( ~EVENP | ~ODDP );
  313.         break;
  314.     }
  315.  
  316.     ioctl( portFD,TIOCSETP,&ttyParms );
  317.  
  318.     return self;
  319. }
  320.  
  321. //========================================================================
  322. - (int) parity;
  323. {
  324.     // The EVENP bit has precedence whether or not the ODDP is set
  325.     if( ttyParms.sg_flags & EVENP )
  326.         return MISC_SP_EVEN;
  327.  
  328.     if( ttyParms.sg_flags & ODDP )
  329.         return MISC_SP_ODD;
  330.     else // <=> none of them are set
  331.         return MISC_SP_NONE;
  332. }
  333.  
  334. //========================================================================
  335. - setMode: ( int )newMode
  336. {    
  337.     switch( newMode )
  338.     {
  339.     case MISC_SP_COOKED:
  340.         ttyParms.sg_flags &= ~CBREAK;
  341.         ttyParms.sg_flags &= ~RAW;
  342.         break;
  343.  
  344.     case MISC_SP_CBREAK:
  345.         ttyParms.sg_flags |= CBREAK;
  346.         ttyParms.sg_flags &= ~RAW;
  347.         break;
  348.  
  349.     case MISC_SP_RAW:
  350.         ttyParms.sg_flags |= RAW;
  351.         ttyParms.sg_flags &= ~CBREAK;
  352.         break;
  353.         
  354.     default:
  355.         ttyStruct.sg_flags &= ~CBREAK;
  356.         ttyStruct.sg_flags &= ~RAW;
  357.         break;
  358.     }
  359.  
  360.     if( flags.connected )    // this flag ensures that the file descriptor is valid
  361.         ioctl( portFD,TIOCSETP,&ttyParms );
  362.  
  363.     return self;
  364. }
  365.  
  366. //========================================================================
  367. - (int) mode;
  368. {
  369.     // I *think * that the RAW bit has precedence over the CBREAK
  370.     if( ttyParms.sg_flags & RAW )
  371.         return MISC_SP_RAW;
  372.  
  373.     if( ttyParms.sg_flags & CBREAK )
  374.         return MISC_SP_CBREAK;
  375.     else // <=> none of them are set
  376.         return MISC_SP_COOKED;
  377. }
  378.  
  379. //========================================================================
  380. - translateNL: ( BOOL )yesNo
  381. {
  382.     if( yesNo )
  383.         ttyParms.sg_flags |= CRMOD;
  384.     else
  385.         ttyParms.sg_flags &= ~CRMOD;
  386.  
  387.     if( flags.connected )    // this flag ensures that the file descriptor is valid
  388.         ioctl( portFD,TIOCSETP,&ttyParms );
  389.  
  390.     status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
  391.  
  392.     return self;
  393. }
  394.  
  395. //========================================================================
  396. - setEchoing: ( BOOL )yesNo
  397. {
  398.     if( yesNo )
  399.         ttyParms.sg_flags |= ECHO;
  400.     else
  401.         ttyParms.sg_flags &= ~ECHO;
  402.  
  403.     if( flags.connected )    // this flag ensures that the file descriptor is valid
  404.         ioctl( portFD,TIOCSETP,&ttyParms );
  405.  
  406.     status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
  407.  
  408.     return self;
  409. }
  410.  
  411. //========================================================================
  412. - setDelegate: theConsumer
  413. {
  414.     // we only accpet the delegate if it responds to the reception notification message
  415.     if( [theConsumer respondsTo:@selector( receiveChars:length: )] )
  416.     {
  417.         delegate = theConsumer;
  418.         status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
  419.         return self;
  420.     }
  421.     delegate=nil;
  422.     status = MISC_SP_OTHER; // this is just not to say we're OK if the delegate was refused.
  423.     
  424.     return nil;  // the return value is the thing to check in this case!
  425. }
  426.  
  427. //========================================================================
  428. - transmitChars: ( const char * )buffer length: ( int )length
  429. {
  430.     if( !flags.connected )
  431.     {
  432.         status = MISC_SP_NOT_OPEN;
  433.         return self;
  434.     }
  435.     
  436.     if( write( portFD,buffer,length ) < 0)
  437.     {
  438.         status = MISC_SP_SEE_ERRNO;
  439.         return self;
  440.     }
  441.  
  442.     status = MISC_SP_OK;
  443.     return self;
  444. }
  445.  
  446. //========================================================================
  447. - ( void )checkReceiver
  448. {
  449. #define BUF_LEN    2048
  450.  
  451.     int        red;
  452.     char    buf[BUF_LEN];
  453.  
  454.     red = read( portFD,buf, BUF_LEN );
  455.  
  456.     // If we got here something HAS arrived at the port I'm not sure if the check is useful
  457.     // On the other hand is this comparison significantly long?
  458.     if( red > 0 )
  459.         [delegate receiveChars: buf length: red];
  460. }
  461.  
  462. //========================================================================
  463. void    *portEventHandler( int fdtag, id thePort )
  464. {
  465.     [thePort checkReceiver];
  466.     return thePort;
  467. }
  468.  
  469. //========================================================================
  470. - suspendIO
  471. {
  472.     if( flags.connected )
  473.     {
  474.         if( !flags.suspended )
  475.             DPSRemoveFD( portFD );
  476.         flags.suspended = YES;
  477.     }
  478.     status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
  479.     return self;
  480. }
  481.  
  482. //========================================================================
  483. - continue { return [self resumeIO];}
  484.  
  485. //========================================================================
  486. - resumeIO
  487. {
  488.     if( flags.connected && flags.suspended )
  489.     {
  490.         DPSAddFD( portFD,( DPSFDProc )portEventHandler, self, NX_MODALRESPTHRESHOLD );
  491.         flags.suspended = NO;
  492.     }
  493.     status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
  494.     return self;
  495. }
  496.  
  497. //========================================================================
  498. - clearStatus;
  499. {
  500.     status = MISC_SP_OK;
  501.     return self;
  502. }
  503.  
  504. - ( BOOL ) isSuspended { return flags.suspended; }
  505. - ( int ) fileDescriptor { return portFD; }
  506. - ( int ) status; { return ( int ) status;}
  507. - ( const char* ) statusString { return "OK!"; }
  508. - ( BOOL ) isConnected { return flags.connected; }
  509. - delegate { return delegate; }
  510. - ( BOOL ) isEchoing { return ( ttyParms.sg_flags & ECHO ); }
  511. - (BOOL) doesTranslateNL { return ( ttyParms.sg_flags & CRMOD ); }
  512. - ( int ) baudRate { return 0; }
  513. - ( const char* ) deviceName { return ( const char* ) device; }
  514.  
  515. @end
  516.  
  517.